package com.example.servlet.aop;

import com.example.aop.entity.OperationLog;
import com.example.aop.service.OperationLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 操作日志切面：自动记录所有标注了 @LogOperation 的方法执行情况
 *
 * 功能亮点：
 *   - 自动获取当前登录用户（从 SecurityContext 或 Token）
 *   - 自动获取请求 IP、User-Agent
 *   - 自动解析方法参数名（无需手动传参）
 *   - 支持 SpEL 表达式动态生成描述
 *   - 支持异步写入，不影响业务性能
 *   - 支持参数/结果的可配置记录
 *
 * 本切面独立于业务，可复用于所有微服务
 */
@Aspect
@Component
public class OperationLogAspect {

    private static final Logger log = LoggerFactory.getLogger(OperationLogAspect.class);

    @Autowired
    private OperationLogService operationLogService;

    @Autowired
    private LocalVariableTableParameterNameDiscoverer discoverer; // ✅ 用于获取方法参数名

    /**
     * 切点：拦截所有标注了 @LogOperation 的方法
     * 限定在 service 和 controller 包下，避免拦截 Spring 内部类
     */
    @Around("@annotation(com.example.aop.annotation.LogOperation)")
    public Object recordOperationLog(ProceedingJoinPoint joinPoint) throws Throwable {

        // ✅ 1. 获取方法签名和注解
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        LogOperation logAnnotation = method.getAnnotation(LogOperation.class);

        // ✅ 2. 获取当前请求上下文（Spring Web 环境）
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes != null ? attributes.getRequest() : null;

        // ✅ 3. 获取操作人信息（假设使用 Spring Security 或 JWT）
        // 实际项目中请替换为你的用户获取逻辑
        String currentUserId = getCurrentUserId();      // 如：从 JWT Token 解析
        String currentUserName = getCurrentUserName();  // 如：从 Redis 或数据库查询

        // ✅ 4. 获取操作元信息
        String module = logAnnotation.module();
        String operationType = logAnnotation.operationType().getDescription();
        String descriptionTemplate = logAnnotation.description();
        boolean recordParams = logAnnotation.recordParams();
        boolean recordResult = logAnnotation.recordResult();
        boolean recordIp = logAnnotation.recordIp();
        boolean recordUserAgent = logAnnotation.recordUserAgent();
        boolean async = logAnnotation.async();

        // ✅ 5. 记录开始时间
        long startTime = System.nanoTime();

        // ✅ 6. 执行目标方法（必须调用）
        Object result = null;
        String status = "SUCCESS";
        String errorMsg = null;

        try {
            result = joinPoint.proceed(); // ✅ 执行业务方法
        } catch (Throwable e) {
            status = "FAILED";
            errorMsg = e.getMessage();
            throw e; // 重新抛出，让上层处理异常
        } finally {
            // ✅ 7. 计算耗时
            long durationMs = (System.nanoTime() - startTime) / 1_000_000;

            // ✅ 8. 构建参数 Map（用于 SpEL 和日志记录）
            Map<String, Object> paramValues = new HashMap<>();
            if (recordParams) {
                String[] paramNames = discoverer.getParameterNames(method);
                Object[] args = joinPoint.getArgs();
                if (paramNames != null) {
                    for (int i = 0; i < paramNames.length; i++) {
                        paramValues.put(paramNames[i], args[i]);
                    }
                }
            }

            // ✅ 9. 构建描述（支持 SpEL 表达式）
            String finalDescription = buildDescription(descriptionTemplate, paramValues, result, currentUserId, currentUserName);

            // ✅ 10. 构建操作日志对象
            OperationLog operationLog = new OperationLog(
                module,
                operationType,
                finalDescription,
                currentUserId,
                currentUserName,
                recordIp && request != null ? getIpAddress(request) : "",
                recordUserAgent && request != null ? request.getHeader("User-Agent") : "",
                recordParams ? toJson(paramValues) : "",
                recordResult ? toJson(result) : "",
                status,
                errorMsg,
                durationMs
            );

            // ✅ 11. 异步写入日志（不阻塞业务）
            if (async) {
                operationLogService.save(operationLog); // ✅ 异步写入
            } else {
                // 同步写入（仅用于测试）
                operationLogService.save(operationLog);
            }
        }

        // ✅ 12. 返回原结果，业务不受影响
        return result;
    }

    // ==================== 辅助方法 ====================

    /**
     * 根据 SpEL 表达式模板和参数动态生成操作描述
     * 支持占位符：{#userId}、{#userName}、{#paramName}、{#result}
     */
    private String buildDescription(String template, Map<String, Object> params, Object result, String userId, String userName) {
        if (template == null || template.trim().isEmpty()) {
            return "执行了 " + getMethodName() + " 方法";
        }

        String desc = template;

        // 替换用户信息
        desc = desc.replace("{#userId}", userId != null ? userId : "未知用户");
        desc = desc.replace("{#userName}", userName != null ? userName : "未知用户");

        // 替换参数值
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            String key = "{#" + entry.getKey() + "}";
            String value = entry.getValue() != null ? entry.getValue().toString() : "null";
            desc = desc.replace(key, value);
        }

        // 替换返回值
        desc = desc.replace("{#result}", result != null ? result.toString() : "null");

        return desc;
    }

    /**
     * 获取当前登录用户ID（示例：从 Spring Security 获取）
     * 实际项目中请替换为你的认证逻辑
     */
    private String getCurrentUserId() {
        // 示例：从 Spring Security 获取
        // Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        // if (auth != null && auth.getPrincipal() instanceof UserDetails) {
        //     return ((UserDetails) auth.getPrincipal()).getUsername();
        // }
        return "test-user-123"; // 模拟
    }

    /**
     * 获取当前登录用户名（示例）
     */
    private String getCurrentUserName() {
        return "张三"; // 模拟
    }

    /**
     * 获取客户端真实 IP（支持 Nginx 代理）
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多个IP时取第一个
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            }
            return ip;
        }
        ip = request.getHeader("X-Real-IP");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }

    /**
     * 将对象转为 JSON 字符串（简化版，实际建议使用 Jackson）
     */
    private String toJson(Object obj) {
        if (obj == null) return "null";
        try {
            return obj.toString(); // 实际项目使用 ObjectMapper.writeValueAsString(obj)
        } catch (Exception e) {
            return "[转换失败: " + e.getMessage() + "]";
        }
    }

    /**
     * 获取方法名（类名.方法名）
     */
    private String getMethodName() {
        // 用于默认描述，可选
        return "";
    }
}